using Baula.Testing.Common;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Baula.TextGen.ActiveTemplating.Tests
{
    [TestClass]
    public class TemplateToVBConverterTests
    {
        private TemplateToVBConverter _converter;

        [TestInitialize]
        public void TestInitialize()
        {
            _converter = new TemplateToVBConverter();
        }

        [TestMethod]
        public void NullString()
        {
            AssertConvert(null, string.Empty);
        }

        [TestMethod]
        public void EmptyString()
        {
            AssertConvert(string.Empty, string.Empty);
        }

        [TestMethod]
        public void LiteralSingleLineCodeWithoutQuotes()
        {
            AssertConvert("Private m_Name As String", @"AppendLine(""Private m_Name As String"")");
        }

        [TestMethod]
        public void NewLine()
        {
            AssertConvert("\r\n", @"AppendLine()
AppendLine()");
        }

        [TestMethod]
        public void LiteralMutlipleLinesCodeWithoutQuotes()
        {
            AssertConvert(@"Private m_Name As String
Private m_DateOfBirth As Date",
                          @"AppendLine(""Private m_Name As String"")
AppendLine(""Private m_DateOfBirth As Date"")");
        }

        [TestMethod]
        public void LiteralSingleLineCodeWithQuotes()
        {
            AssertConvert("m_Name = \"Hello\"", "AppendLine(\"m_Name = \"\"Hello\"\"\")");
        }

        [TestMethod]
        public void LiteralMultipleLinesCodeWithQuotes()
        {
            AssertConvert(@"m_Name = ""Hello""
m_Info = ""Some Info""",
                          @"AppendLine(""m_Name = """"Hello"""""")
AppendLine(""m_Info = """"Some Info"""""")");
        }

        [TestMethod]
        public void JustInlineExpression()
        {
            AssertConvert("{MyExpression}", "AppendLine(MyExpression)");
        }

        [TestMethod]
        public void InlineExpressionAfterLiteral()
        {
            AssertConvert("m_Value = {MyExpression}", "AppendLine(\"m_Value = \" & MyExpression)");
        }

        [TestMethod]
        public void InlineExpressionAtTheVeryBeginning()
        {
            AssertConvert("{MyExpression} some literal", "AppendLine(MyExpression & \" some literal\")");
        }

        [TestMethod]
        public void InlineExpressionAtTheVeryEnd()
        {
            AssertConvert("Some literal {MyExpression}", "AppendLine(\"Some literal \" & MyExpression)");
        }

        [TestMethod]
        public void InlineExpressionBetweenTwoLiterals()
        {
            AssertConvert("Some literal {MyExpression} some other literal", "AppendLine(\"Some literal \" & MyExpression & \" some other literal\")");
        }

        [TestMethod]
        public void SequenceOfTwoInlineExpressions()
        {
            AssertConvert("{My1stExpression}{My2ndExpression}", "AppendLine(My1stExpression & My2ndExpression)");
        }

        [TestMethod]
        public void InlineExpressionContainingString()
        {
            AssertConvert("{My \"really cool\" expression}", "AppendLine(My \"really cool\" expression)");
        }

        [TestMethod]
        public void InlineExpressionContainingStringWithEscapedQuote()
        {
            AssertConvert("{My \"really \"\" cool\" expression}", "AppendLine(My \"really \"\" cool\" expression)");
        }

        [TestMethod]
        public void InlineExpressionMissingClosingBrace_ThrowsException()
        {
            ExceptionAssert.Expect<MissingEndOfInlineExpressionException>(
                () => _converter.Convert("{MyUnclosedInlineExpression")
                );
        }

        [TestMethod]
        public void InlineExpressionMissingClosingBrace_ExceptionHasColumn()
        {
            var exception = ExceptionAssert.Expect<MissingEndOfInlineExpressionException>(
                () => _converter.Convert("{NotClosedPlaceholder")
                );
            Assert.AreEqual("{NotClosedPlaceholder".Length + 1, exception.Column);
        }

        [TestMethod]
        public void InlineExpressionMissingClosingBracePreceededByLiteral_ExceptionHasColumn()
        {
            var exception = ExceptionAssert.Expect<MissingEndOfInlineExpressionException>(
                () => _converter.Convert("This is literal {NotClosedPlaceholder")
                );
            Assert.AreEqual("This is literal {NotClosedPlaceholder".Length + 1, exception.Column);
        }

        [TestMethod]
        public void InlineExpressionWithClosingBraceWithinString_ThrowsException()
        {
            var exception = ExceptionAssert.Expect(
                () => _converter.Convert("{MyUnclosedInlineExpression\"}")
                );
            Assert.IsInstanceOfType(exception, typeof(MissingEndOfStringExpressionException));
        }

        [TestMethod]
        public void InlineExpressionCorrectlyClosedAfterString()
        {
            AssertConvert("{MyExpression \"with string\"}", "AppendLine(MyExpression \"with string\")");
        }

        [TestMethod]
        public void WellKnownInlineExpression_GetsProperlyReplaced()
        {
            const string wellKnownExpression = "TheWellknownExpression";
            const string value = "The value of the well-known expression";

            _converter.AddWellknownExpression(wellKnownExpression, value);
            AssertConvert("{TheWellknownExpression}", "AppendLine(The value of the well-known expression)");
        }

        [TestMethod]
        public void JustSingleLineBlockExpression()
        {
            AssertConvert(
"{{My Expression in all its *!okdf*))) beauty!}}", 
"My Expression in all its *!okdf*))) beauty!");
        }

        [TestMethod]
        public void BlockExpressionAndLiteralOnSeparateLines()
        {
            AssertConvert("{{The block}}\r\nThe literal", "The block\r\nAppendLine(\"The literal\")");
        }

        [TestMethod]
        public void LiteralAndBlockExpressionOnSeparateLines()
        {
            AssertConvert("The literal\r\n{{The block}}", "AppendLine(\"The literal\")\r\nThe block");
        }

        [TestMethod]
        public void LiteralAndBlockExpressionOnSingleLine()
        {
            AssertConvert("The literal before {{Following block}}", 
                "Append(\"The literal before \")\r\nFollowing block");
        }

        [TestMethod]
        public void BlockExpressionWithLiteralAndAnotherBlockExpressionOnSingleLine()
        {
            AssertConvert("{{Opening block}} the literal inbetween {{Closing block}}", 
                "Opening block\r\nAppend(\" the literal inbetween \")\r\nClosing block");
        }

        private void AssertConvert(string template, string expected)
        {
            var converted = _converter.Convert(template);
            Assert.AreEqual(expected, converted);
        }
    }
}